import os
import sys
import jinja2

import model
from utils.file_util import FileUtil
# from uml.uml_transform import UmlTransform

from .gen_context import GenContext
from .gen_module import GenModule
from uml.uml_manager import UmlManager


class Generator:
    namespace_name = "uml"
    statement_global_h = '#include "../umlmodel_global.h"'
    statement_macro_export = "UMLMODEL_EXPORT "
    primitive_types = {
        model.UmlXmiInfo.type_boolean: "bool",
        model.UmlXmiInfo.type_integer: "int",
        model.UmlXmiInfo.type_real: "double",
        model.UmlXmiInfo.type_string: "QString",
        model.UmlXmiInfo.type_unlimited: "int",
    }

    base_element_classes = {"Element", "UMLDiagramElement"}
    di_classes = {"DiagramElement", "Edge", "Shape", "Style", "Diagram"}
    base_class = "BaseElement"
    json_class = "JsonSerializable"

    href_types = {
        "http://www.omg.org/spec/UML/20131001/UML.xmi#": "../uml/",
        "http://www.omg.org/spec/DD/20131001/DI.xmi#": "../di/",
        "http://www.omg.org/spec/DD/20131001/DC.xmi#": "../dc/",
    }

    def __init__(self, output_path: str) -> None:
        self.output_path = output_path
        self.definedClasses: dict[str, model.UmlClass] = {}
        self.definedEnums: dict[str, model.UmlEnumeration] = {}
        self.definedEnumLiterals: dict[str, model.UmlEnumerationLiteral] = {}
        self.class_need_std = False
        self.class_h_includes: dict[str, str] = {}
        self.class_h_classes: dict[str, str] = {}
        self.class_cpp_includes: dict[str, str] = {}
        self.class_attributs: dict[str, model.UmlProperty] = {}
        self.class_operations: dict[str, model.UmlOperation] = {}
        self.class_has_reference: bool = False
        self.class_is_virtual: bool = False
        self.duplicated_attr: dict[str, model.UmlProperty] = {}

        self.env = jinja2.Environment(
            trim_blocks=True,
            lstrip_blocks=True,
            keep_trailing_newline=True,
            loader=jinja2.FileSystemLoader(FileUtil.get_path("gen/templates/cpp")),
        )
        self.env.globals["get_attr_default"] = self.get_attr_default
        self.env.globals["get_attr_from_json"] = self.get_attr_from_json
        self.env.globals["get_attr_has_getter"] = self.get_attr_has_getter
        self.env.globals["get_attr_getter_const"] = self.get_attr_getter_const
        self.env.globals["get_attr_has_setter"] = self.get_attr_has_setter
        self.env.globals["get_attr_identity"] = self.get_attr_identity
        self.env.globals["get_attr_is_primitive"] = self.get_attr_is_primitive
        self.env.globals["get_attr_to_json"] = self.get_attr_to_json
        self.env.globals["get_attr_type"] = self.get_attr_type
        self.env.globals["get_attr_primitive_default"] = self.get_attr_primitive_default
        self.env.globals["get_base_class"] = self.get_base_class
        self.env.globals["get_cls_h_classes"] = self.get_cls_h_classes
        self.env.globals["get_cls_h_includes"] = self.get_cls_h_includes
        self.env.globals["get_comment"] = self.get_comment
        self.env.globals["get_cpp_includes"] = self.get_cpp_includes
        self.env.globals["get_gen_from_json"] = self.get_gen_from_json
        self.env.globals["get_gen_to_json"] = self.get_gen_to_json
        self.env.globals["get_generations"] = self.get_generations
        self.env.globals["get_generations_ctor"] = self.get_generations_ctor
        self.env.globals["get_op_body"] = self.get_op_body
        self.env.globals["get_op_const"] = self.get_op_const
        self.env.globals["get_op_identity"] = self.get_op_identity
        self.env.globals["get_op_params"] = self.get_op_params
        self.env.globals["get_op_qualifier_post"] = self.get_op_qualifier_post
        self.env.globals["get_op_qualifier"] = self.get_op_qualifier
        self.env.globals["get_op_return_type"] = self.get_op_return_type
        self.env.globals["get_op_rules_comment"] = self.get_op_rules_comment
        self.env.globals["get_op_virtual_def"] = self.get_op_virtual_def
        self.env.globals["get_setter_name"] = self.get_setter_name
        self.env.globals["has_op_definition"] = self.has_op_definition
        self.env.globals["has_op"] = self.has_op
        self.env.globals["is_element_child"] = self.is_element_child
        self.env.globals["is_json_serializable_class"] = self.is_json_serializable_class
        self.env.globals["statement_global_h"] = self.statement_global_h
        self.env.globals["statement_macro_export"] = self.statement_macro_export

    def gen_model(self, mod: model.UmlModel, key: str) -> None:
        self.__get_types(mod.packageDict.get(key))
        self.__get_types(mod.profileDict.get(key))
        self.__gen_package(mod.packageDict.get(key))
        self.__gen_package(mod.profileDict.get(key))

    def __gen_package(self, package: model.UmlPackage | model.UmlProfile) -> None:
        if package is None:
            return

        for element in package.packagedElements.values():
            if element.xmi_type == model.UmlXmiInfo.type_package:
                self.__gen_package(element)
            elif element.xmi_type == model.UmlXmiInfo.type_class:
                self.__gen_class(element)
            elif element.xmi_type == model.UmlXmiInfo.type_data_type:
                self.__gen_class(element)
            elif element.xmi_type == model.UmlXmiInfo.type_enumeration:
                self.__gen_enum(element)
            elif element.xmi_type == model.UmlXmiInfo.type_primitive_type:
                pass
            elif element.xmi_type == model.UmlXmiInfo.type_extension:
                self.__gen_extension(element)
            elif element.xmi_type == model.UmlXmiInfo.type_association:
                pass
            elif element.xmi_type == model.UmlXmiInfo.type_package_import:
                pass
            else:
                print(element.xmi_type, element.xmi_id)

    def __gen_class(self, element: model.UmlClass) -> None:
        self.__collect_class(element)
        params: dict = {
            "namespace_name": Generator.namespace_name,
            "entity": element,
        }

        context: GenContext = GenContext(self.output_path)
        context.modules.append(
            GenModule(
                "class.h",
                "class.h.j2",
                f"{element.name}.h",
            )
        )
        context.modules.append(
            GenModule("class.cpp", "class.cpp.j2", f"{element.name}.cpp")
        )

        for module in context.modules:
            template = self.env.get_template(module.template)
            FileUtil.write_text_file_if_change(
                os.path.join(FileUtil.get_path(context.root), module.output_path),
                template.render(params),
            )

    def __gen_enum(self, element: model.UmlEnumeration) -> None:
        params: dict = {"namespace_name": Generator.namespace_name, "entity": element}

        context: GenContext = GenContext(self.output_path)
        context.modules.append(
            GenModule(
                "enum.h",
                "enum.h.j2",
                f"{element.name}.h",
            )
        )

        for module in context.modules:
            template = self.env.get_template(module.template)
            FileUtil.write_text_file_if_change(
                os.path.join(FileUtil.get_path(context.root), module.output_path),
                template.render(params),
            )

    def __get_types(self, package: model.UmlPackage | model.UmlProfile) -> None:
        if package is None:
            return

        for element in package.packagedElements.values():
            if element.xmi_type == model.UmlXmiInfo.type_package:
                self.__get_types(element)
            elif element.xmi_type == model.UmlXmiInfo.type_class:
                self.definedClasses[element.name] = element
            elif element.xmi_type == model.UmlXmiInfo.type_data_type:
                self.definedClasses[element.name] = element
            elif element.xmi_type == model.UmlXmiInfo.type_enumeration:
                self.definedEnums[element.name] = element
                self.__enum_literals(element)
            elif element.xmi_type == model.UmlXmiInfo.type_primitive_type:
                pass
            elif element.xmi_type == model.UmlXmiInfo.type_extension:
                # self.__valid_extension(element)
                pass
            elif element.xmi_type == model.UmlXmiInfo.type_association:
                pass
            elif element.xmi_type == model.UmlXmiInfo.type_package_import:
                pass
            else:
                print(element.xmi_type, element.xmi_id)

    def __enum_literals(self, element: model.UmlEnumeration) -> None:
        for literal in element.ownedLiterals:
            literal.enum = element.name
            self.definedEnumLiterals[literal.xmi_id] = literal

    def __collect_class(self, element: model.UmlClass) -> None:
        self.class_need_std = False
        self.class_has_reference = False
        self.class_is_virtual = False
        self.class_h_includes.clear()
        self.class_h_classes.clear()
        self.class_cpp_includes.clear()
        self.class_attributs.clear()
        self.class_operations.clear()

        if len(element.generalizations) == 0:
            if element.name in Generator.base_element_classes:
                self.class_h_includes[Generator.base_class] = (
                    f'"../umlcore/{Generator.base_class}.h"'
                )
            else:
                self.class_h_includes[Generator.json_class] = (
                    f'"../umlcore/{Generator.json_class}.h"'
                )
        else:
            for gen in element.generalizations:
                self.__add_class_h_includes(element, gen.general)

        for attr in element.ownedAttributes:
            self.__add_class_ref_classes(element, attr.type.href)
            self.class_attributs[attr.name] = attr

        for op in element.ownedOperations:
            if op.isAbstract == "true":
                self.class_is_virtual = True
            self.class_operations[op.name] = op
            for param in op.ownedParameters:
                self.__add_class_ref_classes(element, param.type.href)

    def __add_class_ref_classes(self, cls: model.UmlClass, type_name: str) -> None:
        if type_name == "":
            return
        if type_name == cls.name:
            self.class_need_std = True
            self.class_has_reference = True
            return

        var_type: str | None = Generator.primitive_types.get(type_name)
        if var_type is not None:
            # if var_type == "QString":
            #     self.class_h_includes[var_type] = "<QString>"
            return

        var_type = self.convert_href_type(type_name, False)
        var_type_inc = self.convert_href_type(type_name, True)
        if self.definedEnums.get(type_name) is not None:
            self.__add_class_h_includes(cls, var_type_inc)
        else:
            self.class_h_classes[var_type] = var_type
            self.class_cpp_includes[var_type] = var_type_inc
            self.class_has_reference = True

        if not self.class_need_std and self.definedClasses.get(var_type) is not None:
            self.class_need_std = True

    def __add_class_h_includes(self, cls: model.UmlClass, type_name: str) -> None:
        if type_name == "":
            return
        if type_name == cls.name:
            return

        if type_name in ["BaseElement", "JsonSerializable", "PartialElement"]:
            self.class_h_includes[type_name] = f'"../umlcore/{type_name}.h"'
            return

        var_type: str = Generator.primitive_types.get(type_name)
        if var_type is not None:
            # if var_type == "QString":
            #     self.class_h_includes[var_type] = "<QString>"
            return

        var_type = self.convert_href_type(type_name, True)
        self.class_h_includes[var_type] = f'"{var_type}.h"'

    def get_comment(
        self,
        comment: model.UmlComment,
        indent: str = "    ",
        comment_type: str = "///",
        max_characters: int = 80,
    ) -> str:
        if comment is None:
            return ""

        text = ""
        comment = comment.body.replace("\r\n", "\n").replace(
            "\n", f"\n{indent}{comment_type} "
        )
        words = comment.split(" ")
        line = ""
        for word in words:
            if len(line + word) > max_characters:
                text += f"\n{indent}{comment_type}{line}"
                line = ""
            line += f" {word}"
        text += f"\n{indent}{comment_type}{line}"
        return text

    def get_cls_h_includes(self, element: model.UmlClass) -> str:
        text = ""

        if self.class_need_std:
            text += "#include <memory>\n"
            text += "#include <vector>\n"
            text += "\n"

        list_includes = []
        for cls in self.class_h_includes.values():
            list_includes.append(cls)
        list_includes.sort()

        for cls in list_includes:
            text += f"#include {cls}\n"

        if self.class_need_std:
            text += "\n"
            text += "using std::make_shared;\n"
            text += "using std::shared_ptr;\n"
            text += "using std::vector;\n"

        return text

    def get_cls_h_classes(self, element: model.UmlClass) -> str:
        text = ""

        list_includes = []
        for cls in self.class_h_classes.values():
            list_includes.append(cls)
        list_includes.sort()

        for cls in list_includes:
            text += f"class {cls};\n"

        return text

    def get_base_class(self, element: model.UmlClass) -> str:
        return element.baseClass

    def is_element_child(self, element: model.UmlClass) -> bool:
        if element.isPartialElement:
            return False
        if element.name in Generator.base_element_classes:
            return True
        if element.name in Generator.di_classes:
            return False
        if len(element.generalizations) == 0:
            return False
        return True

    def is_json_serializable_class(self, element: model.UmlClass) -> bool:
        # can = element.name not in UmlTransform.partial_classes.keys()
        return True

    def get_cpp_includes(self, element: model.UmlClass) -> str:
        text = ""

        list_includes = []
        for cls in self.class_cpp_includes.values():
            list_includes.append(cls)
        list_includes.sort()

        for cls in list_includes:
            text += f'#include "{cls}.h"\n'

        return text

    def get_setter_name(self, field: str) -> str:
        return "set" + field[0].upper() + field[1:]

    def get_op_params(self, op: model.UmlOperation) -> str:
        text = ""
        for param in op.ownedParameters:
            if param.direction != "return":
                type_literal = self.__get_type_literal(
                    param.type, self.__get_param_is_array(param)
                )
                text += f"{type_literal} {param.name}, "

        return text[:-2]

    def get_op_return_type(self, op: model.UmlOperation) -> str:
        text = "void"
        for param in op.ownedParameters:
            if param.direction == "return":
                text = self.__get_type_literal(
                    param.type, self.__get_param_is_array(param), False, False, True
                )
                return text

        return text

    def get_op_rules_comment(
        self,
        op: model.UmlOperation,
        indent: str = "    ",
        comment_type: str = "///",
        max_characters: int = 80,
    ) -> str:
        text = ""

        for rule in op.ownedRules:
            text += self.get_op_rule_comment(rule, indent, comment_type, max_characters)
        return text

    def get_op_rule_comment(
        self,
        rule: model.UmlConstraint,
        indent: str = "    ",
        comment_type: str = "///",
        max_characters: int = 80,
    ) -> str:
        text = f"\n{indent}{comment_type} <code>"
        body = rule.specification.body.replace("\r\n", "\n").replace(
            "\n", f"\n{indent}{comment_type} "
        )
        text += f"\n{indent}{comment_type} {body}"
        text += f"\n{indent}{comment_type} </code>"
        return text

    def get_op_body(self, op: model.UmlOperation) -> str:
        return_type = self.get_op_return_type(op)
        text = ""

        match return_type:
            case "bool":
                text = "return false;"
            case "int":
                text = "return 0;"
            case model.UmlXmiInfo.value_literal_unlimited_natural:
                text = "return 0;"
            case "double":
                text = "return 0.0;"
            case "QString":
                text = 'return "";'
            case "void":
                text = ""
            case _:
                enum = self.definedEnums.get(return_type)
                if enum is not None:
                    text = f"return {enum.name}::k{enum.ownedLiterals[0].name};"
                else:
                    text = "return nullptr;"
        return text

    def get_generations(self, cls: model.UmlClass) -> str:
        generations: list[model.UmlGeneralization] = cls.generalizations
        if len(generations) == 0:
            # default inherits
            if cls.name in Generator.base_element_classes:
                return " : public " + Generator.base_class
            else:
                return " : public " + Generator.json_class

        text = ""
        for generation in generations:
            type_name = self.convert_href_type(generation.general)
            if len(text) == 0:
                text = " : public " + type_name
            else:
                text += ", public " + type_name

        return text

    def get_generations_ctor(self, cls: model.UmlClass) -> str:
        generations: list[model.UmlGeneralization] = cls.generalizations
        text = ""
        for generation in generations:
            type_name = self.convert_href_type(generation.general)
            if len(text) == 0:
                text = f"    : {type_name}(umlType)"
            else:
                text += f"\n    , {type_name}(umlType)"

        return text

    def convert_href_type(self, type_name: str, with_path: bool = False) -> str:
        for key, value in Generator.href_types.items():
            if with_path:
                # for include statements
                type_name = type_name.replace(key, value)
            else:
                type_name = type_name.replace(key, "")

        return type_name

    def __get_type_literal(
        self,
        type_obj: model.UmlType,
        is_array: bool,
        is_param=False,
        is_getter=False,
        is_return=False,
    ) -> str:
        # primitive types
        attr_type: str | None = Generator.primitive_types.get(type_obj.href)
        if attr_type is not None:
            if is_array:
                if is_return:
                    return f"shared_ptr<vector<{attr_type}>>"
                else:
                    return f"vector<{attr_type}>{'&' if is_getter else''}"
            if is_param and attr_type == "QString":
                return "const QString&"
            return attr_type

        # handle href type: http://www.omg.org/spec/UML/20131001/UML.xmi#Element.h
        attr_type = self.convert_href_type(type_obj.href)
        if attr_type.startswith("http:"):
            print(attr_type)

        # enum
        if self.definedEnums.get(attr_type) is not None:
            if is_array:
                if is_return:
                    return f"shared_ptr<vector<{attr_type}>>"
                else:
                    return f"vector<{attr_type}>{'&' if is_getter else''}"
            return attr_type

        # class
        if is_array:
            if is_return:
                return f"shared_ptr<vector<{attr_type}>>"
            else:
                return f"vector<shared_ptr<{attr_type}>>{'&' if is_getter else''}"
        return f"shared_ptr<{attr_type}>"

    def __get_param_is_array(self, param: model.UmlParameter) -> bool:
        is_array = param.upperValue is not None
        return is_array

    def get_attr_from_json(self, attr: model.UmlProperty) -> str:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return self.get_attr_from_json_array(attr)

        attr_name = self.get_attr_identity(attr)

        # owner不保存
        if attr_name == "owner":
            return ""

        # primitive types
        attr_type: str | None = Generator.primitive_types.get(attr.type.href)
        if attr_type is not None:
            match attr_type:
                case "bool":
                    return f'{attr_name}_ = json.value("{attr_name}").toBool({attr_name}_default_);'
                case "int":
                    return (
                        f'{attr_name}_ = json.value("{attr_name}").toInt({attr_name}_default_);'
                    )
                case model.UmlXmiInfo.value_literal_unlimited_natural:
                    return (
                        f'{attr_name}_ = json.value("{attr_name}").toInt({attr_name}_default_);'
                    )
                case "double":
                    return f'{attr_name}_ = json.value("{attr_name}").toDouble({attr_name}_default_);'
                case "QString":
                    return f'{attr_name}_ = json.value("{attr_name}").toString({attr_name}_default_);'
                case _:
                    print(f"Error in get_attr_from_json.primitive types: {attr_type}")
                    return ""

        # handle href type: http://www.omg.org/spec/UML/20131001/UML.xmi#Element.h
        attr_type = self.convert_href_type(attr.type.href)
        if attr_type.startswith("http:"):
            print(attr_type)

        # enum
        if self.definedEnums.get(attr_type) is not None:
            return f'{attr_name}_ = ({attr_type})json.value("{attr_name}").toInt((int){attr_name}_);'

        # class
        text = f'{attr_name}_ = JsonSerializable::create<{attr_type}>(json.value("{attr_name}"), errorList);\n'
        # text += f'    {attr_name}_->fromJson(json.value("{attr_name}").toObject(), errorList);'
        return text

    def get_attr_from_json_array(self, attr: model.UmlProperty) -> str:
        # handle href type: http://www.omg.org/spec/UML/20131001/UML.xmi#Element.h
        attr_name = self.get_attr_identity(attr)

        text = f'JsonSerializable::fromJsonArray({attr_name}_, "{attr_name}", json, errorList);'

        return text

    def get_attr_to_json(self, attr: model.UmlProperty) -> str:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return self.get_attr_to_json_array(attr)

        attr_name = self.get_attr_identity(attr)

        # owner不保存
        if attr_name == "owner":
            return ""

        # primitive types
        attr_type: str | None = Generator.primitive_types.get(attr.type.href)
        if attr_type is not None:
            text = f"if ({attr_name}_ != {attr_name}_default_) {{\n"
            text += f'        json.insert("{attr_name}", {attr_name}_);\n'
            text += "    }"
            return text

        # handle href type: http://www.omg.org/spec/UML/20131001/UML.xmi#Element.h
        attr_type = self.convert_href_type(attr.type.href)
        if attr_type.startswith("http:"):
            print(attr_type)

        # enum
        if self.definedEnums.get(attr_type) is not None:
            return f'json.insert("{attr_name}", (int){attr_name}_);'

        # class
        text = f"if ({attr_name}_) {{\n"
        text += f"        QJsonObject {attr_name}_json;\n"
        text += f"        {attr_name}_->toJson({attr_name}_json);\n"
        text += f'        json.insert("{attr_name}", {attr_name}_json);\n'
        text += "    }"
        return text

    def get_attr_to_json_array(self, attr: model.UmlProperty) -> str:
        # handle href type: http://www.omg.org/spec/UML/20131001/UML.xmi#Element.h
        attr_name = self.get_attr_identity(attr)
        text = f'JsonSerializable::toJsonArray({attr_name}_, "{attr_name}", json);'

        return text

    def get_attr_type(self, attr: model.UmlProperty, setter=False, getter=False) -> str:
        return self.__get_type_literal(
            attr.type, UmlManager.get_attr_is_array(attr), setter, getter
        )

    def get_attr_is_primitive(self, attr: model.UmlProperty) -> bool:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return False

        # primitive types
        attr_type: str = Generator.primitive_types.get(attr.type.href)
        return attr_type is not None

    def get_attr_primitive_default(self, attr: model.UmlProperty) -> str:
        """
        对于Primitive类型，返回默认值；

        Args:
            attr (model.UmlProperty): the attribute instance

        Returns:
            str: the default value variable
        """

        attr_type: str = Generator.primitive_types.get(attr.type.href)

        var_value = ""
        if attr.defaultValue is None:
            match attr_type:
                case "bool":
                    var_value = " = false"
                case "int":
                    var_value = " = 0"
                case model.UmlXmiInfo.value_literal_unlimited_natural:
                    var_value = " = 0"
                case "double":
                    var_value = " = 0.0"
                case "QString":
                    var_value = ' = ""'
                case _:
                    print(f"Error in get_attr_default_var.primitive types: {attr_type}")
                    return ""
        else:
            # primitive types
            match attr.defaultValue.xmi_type:
                case model.UmlXmiInfo.value_literal_boolean:
                    if attr.defaultValue.value == "":
                        var_value = " = false"
                    else:
                        var_value = f" = {attr.defaultValue.value}"
                case model.UmlXmiInfo.value_literal_integer:
                    if attr.defaultValue.value == "":
                        var_value = " = 0"
                    else:
                        var_value = f" = {attr.defaultValue.value}"
                case model.UmlXmiInfo.value_literal_unlimited_natural:
                    if attr.defaultValue.value == "":
                        var_value = " = 0"
                    else:
                        var_value = f" = {attr.defaultValue.value}"
                case model.UmlXmiInfo.value_literal_real:
                    if attr.defaultValue.value == "":
                        var_value = " = 0.0"
                    else:
                        var_value = f" = {attr.defaultValue.value}"
                case model.UmlXmiInfo.value_literal_string:
                    if attr.defaultValue.value == "":
                        var_value = ""
                    else:
                        print(
                            f"{sys._getframe().f_code.co_name} the default is not initialized: {attr.defaultValue.xmi_type}"
                        )
                case model.UmlXmiInfo.value_instance_value:
                    return ""
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the default: {attr.defaultValue.xmi_type}"
                    )
                    return ""

        # return
        return var_value

    def get_attr_default(self, attr: model.UmlProperty) -> str:
        """
        Return the attribute's default value.

        Args:
            attr (model.UmlProperty): the attribute instance

        Returns:
            str: the default value
        """
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return self.get_attr_array_default(attr)

        if (
            attr.defaultValue is not None
            and attr.defaultValue.xmi_type == model.UmlXmiInfo.value_instance_value
            and attr.defaultValue.instance != ""
        ):
            enum_inst = self.definedEnumLiterals[attr.defaultValue.instance]
            return f" = {enum_inst.enum}::k{enum_inst.name}"

        # enum: 使用默认值
        attr_type = self.convert_href_type(attr.type.href)
        enum_type = self.definedEnums.get(attr_type)
        if enum_type is not None:
            return f" = {enum_type.name}::k{enum_type.ownedLiterals[0].name}"

        # primitive types
        attr_type: str = Generator.primitive_types.get(attr.type.href)
        if attr_type is None:
            return ""

        var_name = self.get_attr_identity(attr)
        return f" = {var_name}_default_"

    def get_attr_array_default(self, attr: model.UmlProperty) -> str:
        return ""
        # # primitive types
        # var_type: str = Generator.primitive_types.get(attr.type.href)
        # if var_type is not None:
        #     return f" = make_shared<vector<{var_type}>>()"

        # # handle href type: http://www.omg.org/spec/UML/20131001/UML.xmi#Element.h
        # attr_type = self.convert_href_type(attr.type.href)

        # # enum
        # if self.definedEnums.get(attr_type) is not None:
        #     return f" = make_shared<vector<{attr_type}>>()"

        # # class
        # return f" = make_shared<vector<shared_ptr<{attr_type}>>>()"

    def has_op(self, op: model.UmlOperation) -> bool:
        attr = self.class_attributs.get(op.name)
        if attr is not None:
            self.duplicated_attr[op.xmi_id] = op
            # attr.ownedComment = op.ownedComment

        return attr is None

    def get_attr_has_getter(self, attr: model.UmlProperty) -> bool:
        # has_getter = self.class_operations.get(attr.name) is None
        # if not has_getter:
        #     self.duplicated_attr[attr.xmi_id] = attr
        # return has_getter
        return True

    def get_attr_getter_const(self, attr: model.UmlProperty) -> str:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return ""
        return " const"

    def get_attr_has_setter(self, attr: model.UmlProperty) -> bool:
        """
        如果有了Operation定义，则不需要属性的setter方法

        Args:
            attr (model.UmlProperty): _description_

        Returns:
            bool: _description_
        """

        # Array 不需要 setter
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return False

        has_setter = (
            self.class_operations.get(
                self.get_setter_name(self.get_attr_identity(attr))
            )
            is None
        )
        if not has_setter:
            self.duplicated_attr[attr.xmi_id] = attr
        return has_setter

    def get_attr_identity(self, attr: model.UmlProperty) -> str:
        return UmlManager.get_attr_identity(attr)

    def get_op_identity(self, op: model.UmlOperation) -> str:
        return UmlManager.get_op_identity(op)

    def has_op_definition(self, op: model.UmlOperation) -> bool:
        return op.isAbstract != "true"

    def get_op_virtual_def(self, op: model.UmlOperation) -> str:
        if op.isAbstract == "true":
            return " = 0"
        return ""

    def get_op_const(self, op: model.UmlOperation) -> str:
        return " const" if op.isQuery == "true" else ""
        # return ""

    def get_op_qualifier(self, op: model.UmlOperation) -> str:
        return "virtual " if op.redefinedOperation == "" else ""

    def get_op_qualifier_post(self, op: model.UmlOperation) -> str:
        return "" if op.redefinedOperation == "" else " override"

    def get_gen_from_json(self, gen: model.UmlGeneralization) -> str:
        type_name = self.convert_href_type(gen.general)

        # generation
        text = f"{type_name}::fromJson(json, errorList);"
        return text

    def get_gen_to_json(self, gen: model.UmlGeneralization) -> str:
        type_name = self.convert_href_type(gen.general)

        # generation
        text = f"{type_name}::toJson(json);"
        return text
